//
// Created by arnold on 10/28/2018.
//

#include "nmea_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>



/*------------------------- 校验和验证 -------------------------*/
int validate_checksum1(const char *nmea) {
    const char *p = strchr(nmea, '$');
    const char *end = strchr(nmea, '*');
    if (!p || !end || end <= p) return 0;

    unsigned char calc = 0;
    for (p += 1; p < end; p++) calc ^= *p;

    return (calc == (unsigned char)strtoul(end+1, NULL, 16));
}
// 校验和验证
int validate_checksum(const char *nmea) {
    char checksum = 0;
    const char *p = nmea;

    if (*p == '$') p++;

    while (*p != '*' && *p != '\0') {
        checksum ^= *p;
        p++;
    }

    if (*p == '*') {
        int expected = strtol(p+1, NULL, 16);
        return (checksum == expected) ? 0 : 1;
    }
    return 1;
}
/*------------------------- 安全字符串转浮点 -------------------------*/
double safe_atof(const char *str) {
    return (str && *str) ? atof(str) : 0.0;
}

/*------------------------- 度分转十进制 -------------------------*/
double dmm_to_dd(const char *dmm, char dir) {
    double val = safe_atof(dmm);
    int degrees = (int)(val / 100);
    double minutes = val - degrees * 100;
    double dd = degrees + minutes / 60.0;
    return (dir == 'S' || dir == 'W') ? -dd : dd;
}

/*------------------------- 时间解析优化 -------------------------*/
void parse_time(const char *time_str, int *h, int *m, int *s, int *ms) {
    if (!time_str || strlen(time_str) < 6) return;
    sscanf(time_str, "%2d%2d%2d", h, m, s);
    const char *dot = strchr(time_str, '.');
    if (dot && strlen(dot) > 1) {
        *ms = (int)(safe_atof(dot) * 1000);
    }
}

/*------------------------- 主解析函数 -------------------------*/
NMEA_Data parse_nmea(const char *sentence) {
    NMEA_Data data = {0};
    data.status = -1;

    /*---------- 基础校验 ----------*/
    if (!sentence || sentence[0] != '$' || !strchr(sentence, '*')) {
        return data;
    }
    /*---------- 校验和验证 ----------*/
    if (!validate_checksum(sentence)) {
        // printf("校验和验证失败\n");
        // return data;
    }
    /*---------- 分割字段 ----------*/
    char buf[128]={0};
    strncpy(buf, sentence, sizeof(buf)-1);
    #if 0
    int k = 0;
    for (size_t i = 0; i < 128; i++)
    {
        if(buf[i] == '\n') {
            k++;
        }
        if(k > 0) buf[i] = 0;
    }
    #endif

    char *fields[20] = {0};
    int field_count = 0;
    char *ptr = strtok(buf, ",*");
    while (ptr && field_count < 20) {
        fields[field_count++] = ptr;
        ptr = strtok(NULL, ",*");
    }
    /*---------- 识别消息类型 ----------*/
    if (strcmp(fields[0], "$GNGGA") == 0 || strcmp(fields[0], "GNGGA") == 0) {
        data.type = NMEA_TYPE_GNGGA;
        if (field_count != 14) return data;
    }
    else if (strcmp(fields[0], "$GNZDA") == 0 || strcmp(fields[0], "GNZDA") == 0) {
            data.type = NMEA_TYPE_GNZDA;
        if (field_count != 8) return data;
    }
    else {
        return data;
    }
    /*---------- 处理GNGGA数据 ----------*/
    if (data.type == NMEA_TYPE_GNGGA) {
        // 解析时间 (字段1)
        parse_time(fields[1], &data.datetime.hour, &data.datetime.min,
                  &data.datetime.sec, &data.datetime.msec);
        // 解析纬度 (字段2,3)
        data.position.latitude = dmm_to_dd(fields[2], fields[3][0]);
        // 解析经度 (字段4,5)
        data.position.longitude = dmm_to_dd(fields[4], fields[5][0]);
        // 其他定位参数
        data.position.fix_quality = atoi(fields[6]);
        data.position.satellites = atoi(fields[7]);
        data.position.hdop = safe_atof(fields[8]);
        data.position.altitude = safe_atof(fields[9]);
        data.status = 0;
    }
    /*---------- 处理GNZDA数据 ----------*/
    else if (data.type == NMEA_TYPE_GNZDA) {
        // 解析时间 (字段1)
        parse_time(fields[1], &data.datetime.hour, &data.datetime.min,
                  &data.datetime.sec, &data.datetime.msec);

        // 解析日期 (字段2-4)
        data.datetime.day = atoi(fields[2]);
        data.datetime.month = atoi(fields[3]);
        data.datetime.year = atoi(fields[4]);

        // 验证日期有效性
        if (data.datetime.day <1 || data.datetime.day>31 ||
            data.datetime.month <1 || data.datetime.month>12 ||
            data.datetime.year <2000) {
            return data;
        }

        data.status = 0;
    }

    return data;
}

/*------------------------- 打印数据 -------------------------*/
void print_data(const NMEA_Data *data) {
    if (data->status != 0) {
        // printf("解析失败\n");
        return;
    }

    switch (data->type) {
        case NMEA_TYPE_GNGGA:
            printf("[定位数据]\n");
            printf("时间: %02d:%02d:%02d.%03d\n",
                  data->datetime.hour, data->datetime.min,
                  data->datetime.sec, data->datetime.msec);
            printf("纬度: %.6f\n经度: %.6f\n",
                  data->position.latitude, data->position.longitude);
            printf("质量: %d  卫星: %d  HDOP: %.1f\n海拔: %.1f米\n",
                  data->position.fix_quality, data->position.satellites,
                  data->position.hdop, data->position.altitude);
            break;

        case NMEA_TYPE_GNZDA:
            printf("[时间数据]\n");
            printf("日期: %04d-%02d-%02d\n",
                  data->datetime.year, data->datetime.month, data->datetime.day);
            printf("时间: %02d:%02d:%02d.%03d\n",
                  data->datetime.hour, data->datetime.min,
                  data->datetime.sec, data->datetime.msec);
            break;

        default:
            printf("未知数据类型\n");
    }
    printf("-----------------\n");
}

/*------------------------- 测试用例 -------------------------*/
int test() {
    // 测试GNGGA解析
    const char *gngga = "$GNGGA,084306.123,2235.4567,N,11356.1234,E,1,12,0.9,63.5,M,,M,,*4A";
    NMEA_Data BD_Location = parse_nmea(gngga);
    print_data(&BD_Location);

    // 测试GNZDA解析
    const char *gnzda = "$GNZDA,084306.456,15,07,2023,,*7E";
    NMEA_Data BD_Date = parse_nmea(gnzda);
    print_data(&BD_Date);

    return 0;
}